// Octopod
// Copyright (c) 2005-2010
//
// GPL
//
// This file contains a class that provides a secure channel between two peers.

#ifndef OCTOPOD_SECURECHANNEL_H__
#define OCTOPOD_SECURECHANNEL_H__

#include <QSharedPointer>

#include "socket.h"
#include "crypto.h"
#include "share.h"
#include "peer.h"
#include "securechannel_auth_message.pb.h"

namespace octopod {

// Provides functionality to communicate securely between two octopod peers
// after a socket is established between them.  To ensure that a "secure channel"
// isn't created by mistake without authenticating the peer, the constructor
// is private and construction must be done via createOutgoing and createIncoming.
class SecureChannel {
  public:
    // Destroys this secure channel and closes the underlying connection.
    ~SecureChannel();

    // Authenticates and then creates an outgoing secure channel.
    // The provided socket must be connected from an outbound connection.
    // This method will authenticate with the given peer for the given share.
    // If authentication succeeds, returns a new SecureChannel object.
    // Returns NULL otherwise.
    //
    // Takes ownership of the given socket; the caller should not use the socket
    // subsequent to this call.  Destroys the socket either upon failure or upon
    // closure of the created SecureChannel.
    static SecureChannel *createOutgoing(Socket *s,
                                         QSharedPointer<Peer> peer,
                                         QSharedPointer<Share> share);

    // Authenticates and then creates an incoming secure channel.
    // The provided socket must be connected from an inbound connection.
    // This method will authenticate and determine the peer and share.
    // If authentication succeeds, returns a new SecureChannel object.
    // Returns NULL otherwise.
    //
    // Takes ownership of the given socket; the caller should not use the socket
    // subsequent to this call.  Destroys the socket either upon failure or upon
    // closure of the created SecureChannel.
    static SecureChannel *createIncoming(Socket *s);

    // Sends a message via the secure channel.
    // Returns true upon success.
    bool send(const std::string &msg);

    // Receives a message via the secure channel.
    // Stores the message in the given string.
    bool recv(std::string *msg);

  private:
    static const int kNonceLength = 64; // size of nonces for authentication in bytes

    // Constructs a new secure channel over the given socket, to the given
    // peer, in the given share, with the given session key.
    SecureChannel(QSharedPointer<Socket> sock,
                  QSharedPointer<Peer> peer,
                  QSharedPointer<Share> share,
                  const Botan::SymmetricKey &session_key);

    // Sends an encrypted message via the given socket.
    // Returns true if message is written to underlying socket successfully.
    static bool sendEncryptedMessage(QSharedPointer<Socket> sock,
                                     const Botan::SymmetricKey &key,
                                     const std::string &data);

    // A utility routine used by recvEncryptedMessage to get the data
    // for an incoming message, but not actually decrypt it.
    // Returns true upon success.
    static bool recvEncryptedMessageData(QSharedPointer<Socket> sock,
                                         std::string *init_vector,
                                         std::string *ciphertext);

    // Receives an encrypted message from the given socket.
    // Returns true if message is read and decrypted successfully.
    static bool recvEncryptedMessage(QSharedPointer<Socket> sock,
                                     const Botan::SymmetricKey &key,
                                     std::string *data);

    // Receives an encrypted message from the given socket. 
    // Message must have been sent with sendEncryptedMessage.
    // Overwrites data with received message.
    //
    // This overloaded version is to be used for incoming authentication
    // when the share and peer is unknown.
    //
    // Returns true if message is read and decrypted successfully.
    static QSharedPointer<Share> recvEncryptedMessage(QSharedPointer<Socket> sock, 
                                     std::string *data);

    // Utility routine to simplify setting the authentication message.
    static void setAuthMsg(const std::string &from_user_keyhash,
                           const std::string &to_user_keyhash,
                           const std::string &shareid_hash,
                           const std::string &first_nonce,
                           const std::string &second_nonce,
                           SecureChannelAuthMessage *msg);

    // Utility routine to parse and check that the authentication message fields
    // are the right sizes and that the message received parses properly.
    // Also checks that the message is sent to us and is for the right share.
    // Returns true if parsing and checking succeeds.
    static bool processAuthMsg(const std::string &str,
                               QSharedPointer<Peer> me,
                               QSharedPointer<Share> share,
                               SecureChannelAuthMessage *msg);

    // Perform authentication for an outgoing connection.
    // Returns true if authentication succeeds and sets the session key.
    static bool authenticateOutgoing(QSharedPointer<Socket> sock,
                                     QSharedPointer<Peer> peer,
                                     QSharedPointer<Share> share,
                                     Botan::SymmetricKey *session_key);

    // Perform authentication for an incoming connection.
    // Returns the peer and share corresponding to the incoming connection
    // if authentication succeeds, and sets the session key.
    // Returns NULL pointers otherwise.
    static std::pair< QSharedPointer<Peer>, QSharedPointer<Share> > authenticateIncoming(QSharedPointer<Socket> sock, Botan::SymmetricKey *session_key);

    QSharedPointer<Socket> sock_; // the socket connected to our peer
    QSharedPointer<Peer> peer_; // the peer we're connected to
    QSharedPointer<Share> share_; // the share about which we're communicating
    Botan::SymmetricKey session_key_; // the key for this secure channel

    uint32_t outgoing_message_counter_; // counter of next outgoing message
    uint32_t incoming_message_counter_; // counter of next expected incoming message

    DISALLOW_COPY_AND_ASSIGN(SecureChannel);
};

}  // namespace octopod

#endif  // OCTOPOD_SECURECHANNEL_H__
